package net.cs.um;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import net.bat.db.BaseDAO;
import net.bat.db.BaseDAOImpl;
import net.bat.service.IHandle;
import net.bat.upload.uploadServlet;
import net.bat.util.Cfg;
import net.bat.util.Encrypt;
import net.sk.pt.IConsumer;
import net.sk.pt.State;
import net.sk.pt.StateUtil;
import net.sk.web.StateCenter;
import net.sk.ws.WSket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.tools.ant.util.FileUtils;
import org.jabsorb.JSONSerializer;

import dao.hb.MInbox;
import dao.hb.MMessage;
import dao.hb.OAttachment;
import dao.hb.UGrant;
import dao.hb.UUser;
/**
 * 
 * @author chen4w
 *
 */
public class UMService implements IHandle {
	static final String ENTITY_ATTACH="dao.hb.OAttachment";
	static final String PATH_BIND = "bind";
	/**
	 * 令牌参数名
	 */
	public static final String NAME_HANDLE="handleKey";

	private static String NOTICE_BEGIN = "<!--NOTICE BEGIN-->";
	private static String NOTICE_END = "<!--NOTICE END-->";
	private static Log logger = LogFactory.getLog(UMService.class);
	/**
	 * methods
	 */
	public static final char METHOD_NOTICE='N';
	public static final char METHOD_SENDTO_LM='S';
	public static final char METHOD_MESSAGE='M';
	public static final char METHOD_LOGIN ='L';
	public static final char METHOD_LOGOUT ='O';
	public static final char METHOD_CHANGEPLAN ='P';
	public static final char METHOD_VERIFY = 'V';
	public static final char METHOD_BIND = 'B';

	/**
	 * 测试用户id前缀
	 */
	public static final String USER_TEST ="USER_TEST";
	public static int USER_TEST_ID 	= 1000000;
	
	synchronized static int getTestUserId(){
		return USER_TEST_ID++;
	}
	/**
	 * 保存在session中的用户信息所用名称
	 */
	public static final String USER_DEV = "DEV";
	public static final String VALUE_ALL = "全部";
	public static final String EXCEPTION_OPER="越权操作被服务拒绝";
	public static final int OPER_GET=1;
	public static final int OPER_UPDATE=2;
	public static final int OPER_ADD=3;
	/**
	 * 记录数据库表操作权限
	 */
	static private HashMap<Integer,HashMap<String,Integer>> map_oper=new HashMap<Integer,HashMap<String,Integer>>();
	/**
	 * handle和用户id的映射
	 */
	static private HashMap<String,Integer> map_handle_uid = new HashMap<String,Integer>();
	/**
	 * 用户id和sessionId的映射
	 */
	static private HashMap<Integer,String> map_uid_sid = new HashMap<Integer,String>();
	/**
	 * sessionId和用户id的映射
	 */
	static private HashMap<String,Integer> map_sid_uid = new HashMap<String,Integer>();
	/**
	 * MMessage序号
	 */
	static private Integer msgId=0;
	/**
	 * json对象
	 */
	private JSONSerializer ser;
	/**
	 * 向本地服务更新参数的http客户端
	 */
	private DefaultHttpClient httpclient=null;


	/**
	 * 获得用户关联令牌并保存
	 * @param usr
	 */
	static public String setUserHandle(UUser usr,String handle){
		//判断有没有该用户的 handle,如果有就先删除。
		String hold = usr.getHandle();
		if(handle==null){
			handle = UUID.randomUUID().toString();
			logger.info("user enter:"+usr.getUserId());
		}else{
			logger.info("user recover:"+usr.getUserId());
		}
		synchronized(map_handle_uid){
			if(map_handle_uid.containsKey(hold)){
				//删除原来的Handler
				map_handle_uid.remove(hold);
			}
			map_handle_uid.put(handle,usr.getId());
		}
        usr.setHandle(handle);
        return handle;
	}
/**
 * 验证令牌有效性,如果失效则根据令牌恢复用户相关缓存
 * @param request 
 * @return 0:无效令牌 1:从数据库库恢复的令牌——对应浏览器没有退出,服务重新启动  2:内存中有效令牌——对应浏览器刷新
 */
	static public int verifyHandle(HashMap<String, Object> req,HttpServletRequest request){
		String handle=(String)req.get(NAME_HANDLE);	
		if(map_handle_uid.containsKey(handle)) {
			return 2;
		}
		BaseDAOImpl bdi = (BaseDAOImpl) Cfg.getBean("BaseDAOImpl");
		UUser usr= (UUser)bdi.get("from UUser where handle=?", new Object[]{handle});
		if(usr==null) {
			return 0;
		} else{
			loginWithUser(bdi,usr,request,handle);
			return 1;
		}
	}
	/**
	 * 根据sid清除用户相关缓存
	 * @param sid
	 */
	synchronized static public void clearBySid(String sid){
		Integer uid = map_sid_uid.get(sid);
		map_sid_uid.remove(sid);
		if(uid!=null){
			map_uid_sid.remove(uid);
			clearUid(uid);
		}
	}
	/**
	 * 根据uid清除用户相关缓存
	 * @param uid
	 */
	synchronized static  void clearByUid(Integer uid){
		String sid = map_uid_sid.get(uid);
		map_uid_sid.remove(uid);
		if(sid!=null) {
			map_sid_uid.remove(sid);
		}
		clearUid(uid);
	}
	/**
	 * 用户退出,清除用户相光缓存,写数据库退出时间,在线状态
	 * @param uid
	 */
	private static void clearUid(Integer uid){
		BaseDAOImpl bdi = (BaseDAOImpl) Cfg.getBean("BaseDAOImpl");
		UUser usr= (UUser)bdi.get("from UUser where id=?", new Object[]{uid});
		if(usr==null) {
			return;
		}
		map_handle_uid.remove(usr.getHandle());
		usr.setDtLogout(new Date());
		usr.setOnline(0);
		bdi.update(usr);	
		Cfg.getSC().sendSysMsg(null,usr.getName()+Cfg.info("info.usr.left"));
	}
	/**
	 * 关联sid和uid
	 * @param sid sessionId
	 * @param uid 用户id
	 */
	synchronized static  void putUserId(String sid,Integer uid){
		map_uid_sid.put(uid, sid);
		map_sid_uid.put(sid, uid);
	}
	/**
	 * 根据用户id获得sessionId
	 * @param uid 用户id
	 * @return sessionId
	 */
	static public String getSidByUid(Integer uid){
		return map_uid_sid.get(uid);
	}
	/**
	 * 根据sessionId获得用户id
	 * @param sid sessionId
	 * @return 用户id
	 */
	static public Integer getUidBySid(String sid){
		return map_sid_uid.get(sid);
	}
	/**
	 * 获得自增的MMessage Id
	 * @return MMessage Id
	 */
	synchronized static public int getMsgId(){
		return msgId++;
	}
	//数据库操作通用对象
	static private BaseDAO dao;

	public BaseDAO getDao() {
		return dao;
	}

	public void setDao(BaseDAO dao) {
		this.dao = dao;
	}
	
	/**
	 * 获得http客户端并初始化用于http命令参数提交的json对象
	 * @return
	 * @throws Exception
	 */
	private DefaultHttpClient getHttpclient() throws Exception{
		if(httpclient==null){
			httpclient=new DefaultHttpClient();
			ser = new JSONSerializer();
			ser.registerDefaultSerializers();
		}
		return httpclient;
	}
	/**
	 * 根据管理界面命令向本地服务LM发送更新参数请求
	 * @param req 管理界面命令参数
	 * @param request 来自管理界面的http请求
	 * @return 本地服务回应的结果
	 * @throws Exception
	 */
	private String SendLM(HashMap<String, Object> req,HttpServletRequest request) throws Exception{
		Integer sid = (Integer)req.get("id");
		String ipLm=(String)req.get("ipLm");
		List ls_equipment = dao.find("from OEquipment where staId=?",sid);
		List ls_environment = dao.find("from OEnvironment where staId=?",sid);
		HashMap<String,List> cfg = new HashMap<String,List>();
		cfg.put("ls_equipment",ls_equipment);
		cfg.put("ls_environment", ls_environment);

		DefaultHttpClient httpclient = getHttpclient();
		// 目标地址
		HttpPost http_post = new HttpPost("http://"+ipLm+"/update_cfgs");
		List<BasicNameValuePair> formparams = new ArrayList<BasicNameValuePair>();
		formparams.add(new BasicNameValuePair("cfg", ser.toJSON(cfg)));
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
		http_post.setEntity(entity);
		// 执行
		HttpResponse response = httpclient.execute(http_post);
		HttpEntity entity_back = response.getEntity();
		// 显示结果
		BufferedReader reader = new BufferedReader(new InputStreamReader(entity_back.getContent(), "UTF-8"));
		StringBuffer sbuf = new StringBuffer();
		String line;
		while ((line = reader.readLine()) != null) {
			sbuf.append(line);
		}
		if (entity_back != null) {
			entity_back.consumeContent();
		}
		return sbuf.toString();
	}
	/**
	 * 根据session获得UUser对象
	 * @param request http请求
	 * @return 保存在session中的UUser对象
	 * @throws Exception 
	 */
	static public UUser getUserBySession(HttpServletRequest request) throws Exception{
		UUser usr = (UUser)request.getSession().getAttribute(Cfg.SESSION_USER);
		//没有登录信息,则认为是程序员调试
		if(usr==null){
			throw new Exception(Cfg.info("err.usr.timeout"));
			//usr = new UUser();
			//usr.setId(0);
			//usr.setName(USER_DEV);
		}
		return usr;
	}
	
	public String noticeEdit(HashMap<String, Object> req,HttpServletRequest request) throws Exception{
		String path_html = request.getSession().getServletContext().getRealPath("/ipc/main.html");
		String path_html_loc = request.getSession().getServletContext().getRealPath("/ipc/main_loc.html");
		String nt = (String)req.get("notice_content");
		replaceNotice(path_html,nt);
		replaceNotice(path_html_loc,nt);		
		return nt;
	}
	
	private void replaceNotice(String fpath, String notice_content){
		String tpath = fpath + ".tmp";
		try {
			File f = new File(fpath);
			f.renameTo(new File(tpath));
			
			BufferedReader br = new BufferedReader(new InputStreamReader(
					new FileInputStream(tpath), "utf-8"));
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(fpath), "utf-8"));
			String target = null;
			String line;
			while ((line = br.readLine()) != null) {
				if (target == null) {
					target = NOTICE_BEGIN;
				} else if (NOTICE_BEGIN.equals(target)) {
					bw.newLine();
				}
				if (line.indexOf(target) != -1) {
					line = line.substring(0, line.indexOf(target));
					bw.write(line.toString(), 0, line.length());					
					if (NOTICE_BEGIN.equals(target)) {
						bw.write(notice_content.trim());
						target = NOTICE_END;
					} else {
						target = NOTICE_BEGIN;
					}
				} else if (NOTICE_BEGIN.equals(target)) {
					bw.write(line.toString(), 0, line.length());					
				}
			}
			br.close();
			bw.flush();
			bw.close();			
			FileUtils.delete(new File(tpath));
		} catch (FileNotFoundException e) {
			System.out.println(fpath + " not found!!");
		} catch (IOException e) {
			e.printStackTrace();
			FileUtils.delete(new File(fpath));
			File f = new File(tpath);
			f.renameTo(new File(fpath));
		}
	}
	
	/**
	 * 根据管理界面请求向选定(全部)用户发送在线(离线)消息,在线消息通过状态发送机制发送,离线消息写数据库MMessage表和MInbox表
	 * @param req 来自管理界面的请求对象
	 * @param request 来自管理界面的http请求
	 * @return 无
	 * @throws Exception
	 */
	public String SendMessage(HashMap<String, Object> req,HttpServletRequest request) throws Exception{
		//来源用户
		UUser usr = getUserBySession(request);
		//消息题名
		String title = (String)req.get("title");
		//消息内容
		String content = (String)req.get("content");
		//目标用户
		Integer[] sendTo =null;
		Integer sendToGroupId=(Integer)req.get("sendToGroupId");
		String sendToGroup=(String)req.get("sendToGroup");

		if(sendToGroupId==null) {
			sendTo=(Integer[])req.get("to");
		} else{
			List<Integer> ls_grp_usr =dao.find("select id from UUser where gid=?",sendToGroupId);
			sendTo=ls_grp_usr.toArray(new Integer[0]);
		}
		//是否离线消息
		String offline = (String)req.get("offline");
		//发送在线消息
		if(offline==null){
			MMessage  msg = new MMessage();
			msg.setId(getMsgId());
			msg.setTitle(title);
			msg.setContent(content);
			msg.setTmCreate(new Date());
			msg.setUname(usr.getName());
			msg.setOnline(1);
			//发送到所有在线用户
			if(sendTo!=null){//发送到指定用户主界面
				State sta =StateUtil.obj2sta(msg, null, State.ACTION_ADD);
				for (Integer element : sendTo) {
					String sid = getSidByUid(element);
					IConsumer ic = Cfg.getSC().getConsumer(sid);
					if(ic!=null){
						ic.send(null, sta);
					}
				}
			}
		}
		else{//TODO 发送离线消息
			MMessage  msg_po = (MMessage)dao.getDefaultObj("dao.hb.MMessage");
			Integer mid = (Integer)req.get("mid");
			if(mid!=null){
				//设置本消息的父消息
				msg_po.setMid(mid);
				//设置父消息的最新回复时间
				MMessage  msg_mo = (MMessage)dao.get("from MMessage where id=?", mid);
				if(msg_mo!=null){
					msg_mo.setTmReply(new Date());
					UUser me = getUserBySession(request);
					msg_mo.setRname(me.getName());
					dao.save(msg_mo);
				}
			}
			msg_po.setTitle(title);
			msg_po.setContent(content);
			msg_po.setOnline(0);
			msg_po.setTmCreate(new Date());
			msg_po.setUid(usr.getId());
			msg_po.setUname(usr.getName());
			if(sendToGroup!=null) {
				msg_po.setSendTo(sendToGroup);
			}
			dao.save(msg_po);
			//发送到所有用户
			if(sendTo!=null){
				State s =StateUtil.obj2sta(msg_po, new String[]{"id","uname","title","content"}, State.ACTION_PUT);
				s.getItems().put("time", new Date().getTime());
				s.setCn(StateCenter.PR_SYS_MSG);
				//发送到指定用户
				for (Integer element : sendTo) {
					MInbox mb = (MInbox)dao.getDefaultObj("dao.hb.MInbox");
					mb.setMid(msg_po.getId());
					mb.setUid(element);
					dao.save(mb);
					//同时向在线用户发送消息
					String sid = getSidByUid(element);
					HashMap<Object,WSket> wsMap = Cfg.getSC().getWSMap(sid);
					if(wsMap!=null){
						Iterator<Entry<Object, WSket>> iter = wsMap.entrySet().iterator(); 
						while (iter.hasNext()) { 
							Entry<Object, WSket> entry = iter.next(); 
							WSket cur = entry.getValue(); 
							cur.send(null, s);
						} 							
					}
				}				
			}
		}
		return null;
	}
/**
 * 增加用户对数据库表的操作权限
 * @param userId 用户id
 * @param entity 数据库表
 * @param oper 操作权限
 */
	static public void putOper(Integer userId,String entity,Integer oper){
		HashMap<String,Integer> map_user = map_oper.get(userId);
		if(map_user==null){
			map_user = new HashMap<String,Integer>();
			map_oper.put(userId, map_user);
		}
		map_user.put(entity, oper);
	}
	/**
	 * 获得用户对数据库表的操作权限
	 * @param userId 用户id
	 * @param entity 数据库表
	 * @return 操作权限
	 */
	static public Integer getOper(Integer userId,String entity){
		HashMap<String,Integer> map_user = map_oper.get(userId);
		if(map_user==null) {
			return null;
		}
		Integer op = map_user.get(entity);
		if(op!=null) {
			return op;
		}
		return map_user.get(VALUE_ALL);
	}
	static public boolean hasOper(Integer userId,String entity,int oper){
		Integer op= getOper(userId,entity);
		if(op!=null && op>=oper) {
			return true;
		} else {
			return false;
		}
	}
	static public void checkOper(UUser user,String entity,int oper) throws Exception{
		if(user.getUserId().startsWith(USER_TEST)) {
			return;
		}
		checkOper(user.getId(),entity,oper);
	}
	static private void checkOper(Integer userId,String entity,int oper) throws Exception{
		if(!hasOper(userId, entity, oper)) {
			throw new Exception(EXCEPTION_OPER);
		}
	}
	/**
	 * 根据管理界面请求注销用户
	 * @param req 来自管理界面的请求对象
	 * @param request 来自管理界面的http请求
	 * @return 无
	 */
	private String logout(HashMap<String, Object> req,HttpServletRequest request){
		String sid = request.getSession().getId();
		Integer uid = getUidBySid(sid);
		if(uid==null) {
			return null;
		}
		request.getSession().setAttribute(Cfg.SESSION_USER, null);
		clearByUid(uid);
		return null;		
	}
	/**
	 * 用户登录成功之后的操作
	 * @param bdi dao对象
	 * @param usr 用户对象
	 * @param request http请求
	 * @return 字符串数组 [用户所在组,收件箱数量,令牌,菜单权限1,菜单权限2...]
	 */
	static public Object[] loginWithUser(BaseDAOImpl bdi,UUser usr,HttpServletRequest request,String handle){
		Object[] ms;
		//测试用户登录
		if(usr.getUserId().startsWith(USER_TEST)){
			ms = new String[1+4];
			ms[0]="admin";
			ms[1]="0";
			ms[2]=setUserHandle(usr,handle);
			ms[3]="admin_name";
			ms[4]=0;
			ms[5]="1";
			request.getSession().setAttribute(Cfg.SESSION_USER, usr);
			putUserId(request.getSession().getId(),usr.getId());
			return ms;
		}
		String group_name= (String)bdi.get("select name from UGroup where id=?", usr.getGid());
		List<UGrant> ls_grant = bdi.find("from UGrant where gid=?", usr.getGid());
		int len = ls_grant.size();
		ms = new Object[len+5];
		//用户组
		ms[0]=group_name;
		ms[1]=dao.getCountByQuery("from MInbox A, MMessage B where A.mid=B.id and B.tmReply is null  and A.uid=?",new Object[]{usr.getId()}).toString();
		//handle 浏览器未关闭服务重启不应重设handle
		ms[2]=setUserHandle(usr,handle);
		ms[3]=usr.getName();
		ms[4]=usr.getId();
		//根据用户所在组获得权限,返回菜单权限到前台,设置数据库表权限到后台
		for(int i=0; i<len; i++){
			UGrant cur = ls_grant.get(i);
			String pm_menu =cur.getPmMenu(); 
			ms[i+5]=pm_menu;
			putOper(usr.getId(),cur.getPmEntity(),cur.getPmOper());
		}
		//清除可能存在的缓存
		Cfg.getSC().sendSysMsg(null,usr.getName()+Cfg.info("info.usr.enter"));
		usr.setDtLogin(new Date());
		usr.setOnline(1);
		usr.setIpaddr(getRemortIP(request));
		//分配handle
		bdi.update(usr);
		request.getSession().setAttribute(Cfg.SESSION_USER, usr);
		putUserId(request.getSession().getId(),usr.getId());
		return ms;
	}
	
	static private String getRemortIP(HttpServletRequest request) {
	  if (request.getHeader("x-forwarded-for") == null) {
	   return request.getRemoteAddr();
	  }
	  return request.getHeader("x-forwarded-for");
	}
	/**
	 * 根据管理界面请求登录处理
	 * @param req 来自管理界面的请求对象
	 * @param request 来自管理界面的http请求
	 * @return 字符串数组 [用户所在组,收件箱数量,令牌,菜单权限1,菜单权限2...]
	 * @throws Exception 
	 */
	private Object[] login(HashMap<String, Object> req,HttpServletRequest request) throws Exception{
		UUser usr;
		String userId = (String)req.get("user");
		//正常登录
		if(!userId.startsWith(USER_TEST)){
			String pwd = (String)req.get("pwd");
			String pwd_ept = Encrypt.calcUserEncrypt(userId, pwd);
			usr= (UUser)getDao().get("from UUser where userId=? and pwd=? ", new Object[]{userId,pwd_ept});
			if(usr==null) {
				throw new Exception(Cfg.info("err.usr.missing"));
			}
			if(usr.getStatus()!=null && usr.getStatus()==0) {
				throw new Exception(Cfg.info("err.usr.disable"));
			}
			
			String changePwd=(String)req.get("changepwd");
			if(changePwd!=null && changePwd.equals("on")){
				String pwd1=(String)req.get("pwd1");
				String pwd1_ept = Encrypt.calcUserEncrypt(userId, pwd1);
				usr.setPwd(pwd1_ept);
			}
		}else{//测试用户登录
			usr = new UUser();
			usr.setId(getTestUserId());
			usr.setUserId(userId);
			usr.setId(0);
		}
		return loginWithUser((BaseDAOImpl)getDao(),usr,request,null);
	}
	
	
	
	private String bind(HashMap<String, Object> req,HttpServletRequest request) throws Exception{
		String str_pid = (String)req.get("pid");
		if(str_pid==null){
			return "缺少主附件id";
		}
		int pid = Integer.parseInt(str_pid);
		String str_jid = (String)req.get("jid");
		String ct = (String)req.get("ct");
		OAttachment  jpo;
		//新建
		if(str_jid==null){
			jpo = (OAttachment)dao.getDefaultObj(ENTITY_ATTACH);
			jpo.setEntityName(ENTITY_ATTACH);
			jpo.setEntityId(pid);
			jpo.setStatus(1);
			jpo.setStype("3");
			jpo.setName("bind");
			jpo.setTmCreate(new Date());
			dao.save(jpo);
			str_jid=jpo.getId().toString();
			jpo.setPath(uploadServlet.PATH_SPLITER
					//+PATH_BIND+PATH_SPLITER
					+jpo.getId()+".js");
					
			dao.update(jpo);
		}else{//
			jpo=(OAttachment)dao.loadPO(ENTITY_ATTACH, str_jid);
			jpo.setTmModify(new Date());
		}
		//确认目录存在,不存在则建立
/*		String path_bind = uploadServlet.getWorkPath()+PATH_BIND;
		File fpb = new File(path_bind);
		if(!fpb.exists()||fpb.isFile()){
			fpb.mkdir();
		}
*/		//save content
		String fpath = uploadServlet.getWorkPath()
				+str_jid+".js";
	     try  { 
	         FileOutputStream fos =new  FileOutputStream(fpath); 
	         OutputStreamWriter osw =new  OutputStreamWriter(fos,"UTF-8"); 
	         osw.write(ct); 
	         osw.flush(); 
	     }  catch  (Exception e) { 
	         e.printStackTrace(); 
	     }		
		return fpath+" "+ct.length();		
	}
	
	
	
	@Override
	public Object handle(char method, HashMap<String, Object> rm,
			HttpServletRequest request) throws Exception {
		// TODO Auto-generated method stub
		switch(method){
		case METHOD_LOGIN:
			return login(rm,request);
		case METHOD_LOGOUT:
			return logout(rm,request);
		case METHOD_MESSAGE:
			return SendMessage(rm,request);
		case METHOD_SENDTO_LM:
			return SendLM(rm,request);
		case METHOD_NOTICE:
			return noticeEdit(rm,request);
		case METHOD_VERIFY:
			return verifyHandle(rm, request);
		case METHOD_BIND:
			return bind(rm,request);
		}
		return null;
	}

}
